/*
 * Copyright 2018 Zhongan.com All right reserved. This software is the
 * confidential and proprietary information of Zhongan.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Zhongan.com.
 */

package com.zhongan.cloud.gateway.filter.limit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import reactor.core.publisher.Mono;

/**
 * 访问限流
 *
 * @author tongyufu
 */
@Configuration
public class RateLimitFilter {

    @Autowired
    private RemoteAddressKeyResolver remoteAddrKeyResolver;

    /**
     * 限流
     *
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator rateLimitRouteLocator(RouteLocatorBuilder builder) {
        //org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory
        return builder
                .routes()
                .route(spec -> spec
                        .path("/limit/**")
                        .filters(
                                f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter()).setKeyResolver(
                                        hostAddressKeyResolver()))).uri("lb://HELLO-SERVICE")
                        .id("requestratelimiter_route")).build();
    }

    /**
     * 限流策略。 限流原理：该方法返回的值会作为Redis的key，用来统计访问次数，进而实现限流。 注意：每个key的访问次数是独立计算的。
     * 如果返回空值(empty)，则直接被拒绝。
     */
    @Bean
    public KeyResolver hostAddressKeyResolver() {
        return exchange -> Mono.justOrEmpty(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }

    /**
     * https://juejin.im/entry/5ac9401e5188255583597fd7<br>
     * replenishRate：每秒补充令牌的数量<br>
     * burstCapacity：令牌桶容量，超过此容量的令牌会被丢弃<br>
     * 例如：replenishRate为2，burstCapacity为4，则第一秒时允许4个请求，之后每秒只允许2个请求。
     * 若中间停了2秒，则下一秒又允许4个请求（每秒补充2个令牌）<br>
     * GatewayRedisAutoConfiguration加载 META-INF/scripts/request_rate_limiter.lua
     * 路径下的 Redis Lua 脚本。 该脚本使用 Redis 基于令牌桶算法实现限流。
     *
     * @return
     */
    @Bean
    RedisRateLimiter redisRateLimiter() {
        return new RedisRateLimiter(2, 3);
    }

}
